﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Skylabs.Net;

namespace Skylabs.Breeze
{
    public class FileTransferer
    {
        public String Host { get; set; }
        public string FileName { get; set; }
        public string FilePath { get; set; }
        public string Hash { get; set; }
        public FileStream File { get; set; }
        public List<TransferClient> Clients { get; set; }
        public const int BufferSize = 1024*1024;
        public int TotalPacketsSent = 0;
        private byte[] Buffer = new byte[BufferSize];
        public long FileSize{get; private set; }
        public long TotalBytesSent{get; private set; }
        private long curPosition = 0;
        private int clientNum = 0;
        public int Progress
        {
            get
            {
                return (int)(((double)TotalBytesSent / (double)FileSize) * 100d);
            }
        }
        public event EventHandler OnComplete;
        public FileTransferer()
        {
            
        }
        public FileTransferer(string fileName, string host)
        {
            FilePath = fileName;
            FileInfo f = new FileInfo(fileName);
            FileName = f.Name;
            Host = host;
            TotalBytesSent = 0;
            try
            {
                File = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, FileOptions.SequentialScan);
            }
            catch (Exception e)
            {
                ErrorWindow er = new ErrorWindow(e);
                er.ShowDialog();
            }
            
        }
        public SocketMessage Grab_Next_Data_Chunk()
        {
            lock (File)
            {
                File.Position = curPosition;
                if (curPosition >= FileSize - 1)
                    return null;
                int count = File.Read(Buffer, 0, (FileSize - curPosition) >= FileTransferer.BufferSize ? FileTransferer.BufferSize : (int)(FileSize - curPosition) );
                TotalBytesSent += count;
                SocketMessage sm = new SocketMessage("filepart");
                sm.AddData("filename", FileName);
                sm.AddData("data", (byte[])Buffer.Clone());
                sm.AddData("size",count);
                sm.AddData("position", curPosition);
                curPosition += count;
                TotalPacketsSent++;
                return sm;
            }
        }
        public void Start(int ConnectionCount)
        {
            Program.ServerTrace.TraceInformation("Creating Connections.");
            Create_Connections(ConnectionCount);
            File.Seek(0, SeekOrigin.Begin);
            FileSize = File.Length;
            Clients[0].Start(this,0);
            SocketMessage sm = new SocketMessage("filestart");
            sm.AddData("filename", FileName);
            sm.AddData("size", FileSize);
            sm.AddData("hash", "fun");
            Clients[0].WriteMessage(sm);
            Program.ServerTrace.TraceInformation("Sent start packet");

            for (clientNum = 1; clientNum < ConnectionCount; clientNum++)
            {
                Clients[clientNum].Start(this, clientNum);
            }

        }
        private void Create_Connections(int count)
        {
            Clients = new List<TransferClient>();
            for (int i = 0; i < count; i++)
            {
                TransferClient tc = new TransferClient();
                tc.Connect(Host, 7678);
                Clients.Add(tc);
            }
        }
        public void AddClient()
        {
            TransferClient tc = new TransferClient();
            tc.Connect(Host, 7678);
            tc.Start(this, clientNum);
            clientNum++;
            Clients.Add(tc);
        }
        public void RemoveClient()
        {
            Clients.Last().Kill();
        }
        public void AdjustClientCount(int newCount)
        {
            int dif = newCount - Clients.Count;
            if (dif > 0)
            {
                for(int i=0;i<dif;i++)
                    AddClient();
            }
            else
            {
                for(int i=0;i<Math.Abs(dif);i++)
                    RemoveClient();
            }
        }
        public void ClientDone(TransferClient tc)
        {
            SocketMessage sm = new SocketMessage("partdone");
            sm.AddData("filename",FileName);
            tc.WriteMessage(sm);
            tc.Close(DisconnectReason.CleanDisconnect);
            Clients.Remove(tc);
            if (Clients.Count == 0)
            {
                Program.ServerTrace.TraceInformation("File '{0}' Transfered.\nTotal Packets Sent: {1}", FilePath,
                                                     TotalPacketsSent);
                File.Close();
                File.Dispose();
                if(OnComplete != null)
                    OnComplete.Invoke(this,null);
            }

        }

    }
    public class TransferClient : Skylabs.Net.Sockets.SkySocket,IEquatable<TransferClient>
    {
        public FileTransferer Parent;
        public int ID;
        private bool KeepRunning = true;
        public Thread Runner;
        public void Start(FileTransferer parent, int id)
        {
            Parent = parent;
            ID = id;
            SocketMessage sm = new SocketMessage("hello");
            sm.AddData("filename",Parent.FileName);
            WriteMessage(sm);
        }
        public void Kill()
        {
            KeepRunning = false;
        }
        private void run()
        {
            while (KeepRunning)
            {
                SocketMessage sm = Parent.Grab_Next_Data_Chunk();
                if (sm == null)
                    break;
                WriteMessage(sm); 
                Thread.Sleep(10);
            }
            Parent.ClientDone(this);
        }
        public override void OnMessageReceived(Net.SocketMessage sm)
        {
            if (sm.Header == "start")
            {
                Runner = new Thread(run);
                Runner.Start();
            }
        }

        public override void OnDisconnect(Net.DisconnectReason reason)
        {
            
        }

        public bool Equals(TransferClient other)
        {
            return this.ID == other.ID;
        }
    }
}
